package com.ltf.config;

import com.ltf.Utils.JwtUtil;
import com.ltf.Utils.PackJson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

//继承重写 SpringSecurity 已经存在的 账号密码认证过滤器
public class JwtLoginFiter extends UsernamePasswordAuthenticationFilter {
    //打印输出换成这个
    private Logger logger=LoggerFactory.getLogger(this.getClass());

    AuthenticationManager authenticationManager;
//    new JwtLoginFiter(authenticationConfiguration.getAuthenticationManager())
    public JwtLoginFiter(AuthenticationManager manager) {
        System.out.println("获取认证管理器："+manager);
        this.authenticationManager=manager;
        //.loginProcessingUrl("/login") //重要  session模式才有效果  因为重写，这个不生效
        //在这里通知 SpringSceurtiy，登录的请求路径。
        super.setFilterProcessesUrl("/api/login");//super 明确调用父对象方法
    }

    //准备去认证 attempt 尝试  Authertication 认证
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {

        //获取浏览器登录的账号密码
        String username = obtainUsername(request);
        String password = obtainPassword(request);
//        request.getParameter("登录方式") 支持账号登录，也支持短信验证码登录
//        System.out.println("登录的账号="+username);
//        System.out.println("登录的密码="+password);
        logger.debug("登录的账号="+username);
        logger.debug("登录的密码="+password);

        //loadUserByUsername(String username)
        //创建 SpringSecurity 内部的登录令牌
        UsernamePasswordAuthenticationToken loginToken=
                new UsernamePasswordAuthenticationToken(username,password);
        //要让SpringSecurity的登录（认证）管理器去认证
        //（认证）管理器去调用 loadUserByUsername(String username)
        Authentication authenticate = authenticationManager.authenticate(loginToken);
        logger.debug("认证成功返回的对象，session 模式，放在=====》");
        logger.debug(authenticate.toString());
        logger.debug("=======认证成功返回的对象");
        return authenticate; //返回登录成功的对象，不成功没有对象

    }
    //多态：继承 重写 向上转型  目的调用子类【对象】方法

    //表示认证成功登录的 调用如下方法
    //authenticationManager.authenticate(loginToken);
    @Override
    protected void successfulAuthentication(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain,
            Authentication authResult) throws IOException, ServletException {
        System.out.println("authenticationManager.authenticate(loginToken): 表示认证成功登录的 调用如下方法");
        //创建 Token
//        String token = JwtUtil.createToken(账号，密码);
        // 再把Token 分组成 JSON 响应 浏览器 （axios,qpipost）
        logger.warn("登录成功="+authResult);
        //登录成功对象，里面有 username，有角色
        logger.info(authResult.getPrincipal().toString());
        Object principal = authResult.getPrincipal();
        User user=(User) principal;
        logger.info("登录成功="+user.getUsername());
        logger.info("登录成功="+user.getAuthorities());

        List<String> roles=new ArrayList<>();
        for (GrantedAuthority sga:user.getAuthorities()){
            logger.debug("sga.toString()"+sga.toString());
            roles.add(sga.toString());
        }
        String token = JwtUtil.createToken(user.getUsername(),roles);
        String json = PackJson.packJson(200, "登录成功", token);
        //不写默认就是200
        response.setStatus(200);//这个才是真正响应给浏览器的，上面的只是自己定义的
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(json);
        response.flushBuffer();//刷新一下

    }
    //表示认证失败登录的 调用如下方法
    //authenticationManager.authenticate(loginToken);
    @Override
    protected void unsuccessfulAuthentication(
            HttpServletRequest request,
            HttpServletResponse response,
            AuthenticationException failed) throws IOException, ServletException {
       logger.debug("authenticationManager.authenticate(loginToken): 表示认证失败登录的 调用如下方法");
        response.setContentType("text/html;charset=UTF-8"); //输出格式
        response.setStatus(401);//设置状态码
        //@ResponseBody 实现原理，自己把对象转成JSON字符串，再用 response 响应浏览器
        //msg,data,code 3个成员变量

        String str=PackJson.packJson(401,"认证失败，账号或密码错误","");
        response.getWriter().write(str);
        response.flushBuffer();//刷新一下
    }
}
